/**
* @file vga_text.c
* VGA compatible text mode driver
*
* Copyrights 2010 Michal Saman, m.saman@designplus.cz.
* This source code is release under the Apache License 2.0.
* http://www.apache.org/licenses/LICENSE-2.0
*/

#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <drivers/vga/vga_text.h>

static unsigned int _xPos=0, _yPos=0;
static unsigned _startX=0, _startY=0;
static unsigned _color=0;

/**
* Prints character on screen.
* @param c Character to print.
*/
void vga_text_mode_putc(unsigned char c) {
	bool move = true;
	if (c==0)
		return;
	if (c == '\b') {
		// begin of line - roll back cursor to the end of previous line
		if (_xPos==0) {
			_xPos = COLS;
			if (_yPos > 0) _yPos--;
		}
		// normal position - move cursor back
		if (_xPos>0)
			_xPos--;
		c = 0x20;
		move = false;
	} else if ((c == '\n'||c=='\r')||(_xPos > COLS-1)) { /* start new line */
		if (_yPos==LINES-1)
			vga_text_mode_scroll_line();
		else
			_yPos+=1;
		_xPos=_startX;
		if ((c == '\n'||c=='\r')) {
			c = 0x20;
			move = false;
		}
	}

	/* insert character to video memory - draw it */
	unsigned char *p = (unsigned char*) VGA_TEXT_MODE_VID_MEMORY + (_xPos)*2 + _yPos * COLS*2;
	*p++ = c;
	*p = _color;

	if (move)
		_xPos++;
}

/**
* Print string on screen.
* @param String to print.
*/
void vga_text_mode_puts(char *str) {
	if (!str)
		return;
	size_t i;
	for (i=0; i<strlen(str); i++) {
		vga_text_mode_putc(str[i]);
	}
}

/**
* Scroll display to one line up and clear last line.
*/
void vga_text_mode_scroll_line() {
	unsigned char *p = (unsigned char*) VGA_TEXT_MODE_VID_MEMORY;
	/* scroll up one line */
	int i;
	for (i=0;i<LINES*COLS*2-160;i++) {
		p[i] = p[i+160];
	}
	/* clear last line */
	for (i=LINES*COLS*2-160;i<LINES*COLS*2;i+=2) {
		p[i] = ' ';
		p[i+1] = _color;
	}
}

/**
* Gets current CRT cursor position and store it in our position variables.
*/
void vga_text_mode_get_cursor_pos() {
	char x,y;
	__asm__ volatile (
		"xor		%%eax, %%eax\n\t"

		"mov		$0x0F, %%al\n\t"			// cursor location low byte
		"mov		$0x03D4, %%dx\n\t"			// port of CRT index register
		"out		%%al, %%dx\n\t"

		"mov		$0x03D5, %%dx\n\t"			// read it from data register
		"in			%%dx, %%al\n\t"
		"mov		%%al, %%bl\n\t"				// store it in BL

		"xor		%%eax, %%eax\n\t"

		"mov		$0x0E, %%al\n\t"			// cursor location high byte
		"mov		$0x03D4, %%dx\n\t"			// port of CRT index register
		"out		%%al, %%dx\n\t"

		"mov		$0x03D5, %%dx\n\t"			// read it from data register
		"in			%%dx, %%al\n\t"
		"mov		%%al, %%bh\n\t"				// store it in BH

		"xor		%%eax, %%eax\n\t"

		"mov		%%bx, %%ax\n\t"
		"mov		%2, %%cl\n\t"
		"div		%%cl\n\t"					// celociselne deleni AX = AX \ COLS
												// y = al, x = ah
		"mov		%%al, %0\n\t"				// vysledek celociselneho deleli = Y;
		"mov		%%ah, %1\n\t"				// zbytek celociselneho deleni = X;
		: "=q"(y), "=q"(x)
		: "n"(COLS)
	);
	_yPos = (int)y;
	_xPos = (int)x;
}

/**
 * Returns current position of console X axis cursor.
 */
uint32_t vga_text_mode_cursor_getX() {
	return _xPos;
}

/**
 * Returns current position of console Y axis cursor.
 */
uint32_t vga_text_mode_cursor_getY() {
	return _yPos;
}

/**
* Set font color which is use for output.
* @param c Code of color (4bit for backgroud, 4bit for font color).
*/
unsigned vga_text_mode_set_color(const unsigned c) {
	unsigned t = _color;
	_color = c;
	return t;
}

/**
* Set the virtual cursor X axis position to 0.
*/
void vga_text_mode_cursor_resetX() {
	_xPos = 0;
	_startX = 0;
}

/**
* Set output virtual cursor to X,Y.
*/
void vga_text_mode_gotoXY(unsigned x, unsigned y) {
	_xPos = x;
	_yPos = y;
	_startX = _xPos;
	_startY = _yPos;
}

/**
* Clear screen with specified color.
* @param c Color code
*/
void vga_text_mode_clr_scr(const unsigned short c) {
	unsigned char *p = (unsigned char*)VGA_TEXT_MODE_VID_MEMORY;
	int i;
	for (i=0; i<160*26; i+=2) {
		p[i] = ' ';
		p[i+1] = c;
	}
	_xPos=_startX; _yPos=_startY;
}
